<template>
  <div
    class="context-menu"
    ref="contextMenu"
    v-show="isShow"
    :style="{ transform: transformStyle }"
  >
    <ul class="operations">
      <li
        class="operation"
        v-for="(item, index) in opterations"
        :key="index"
        @click="(e) => !item.disabled && item.handler(e)"
        :class="{ disabled: item.disabled }"
      >
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { watch, ref, onMounted, useTemplateRef, shallowRef, nextTick } from 'vue'
import { useFabricStore } from '@/stores/fabric'
import { storeToRefs } from 'pinia'
import * as fabric from 'fabric'
import { onClickOutside } from '@vueuse/core'
import {
  getClonedObject,
  deleteObject,
  globalBossIds,
  globalMinionIds,
} from '@/utils/fabricOprationTools'

const contextMenu = useTemplateRef('contextMenu')

const fabricStore = useFabricStore()
const { setEditPanelVisible } = fabricStore
const { canvas, editPanelVisible } = storeToRefs(fabricStore)

const isShow = ref(false)
const left = ref('')
const top = ref('')

const clipboard = shallowRef(null)

onClickOutside(contextMenu, () => {
  isShow.value = false
})

const opterations = [
  {
    name: '编辑',
    disabled: false,
    handler: () => {
      isShow.value = false
      setEditPanelVisible(true)
      console.log(editPanelVisible.value)
    },
  },
  {
    name: '复制',
    disabled: false,
    handler: async () => {
      isShow.value = false
      clearClipboardIds()
      const selected = canvas.value.getActiveObject()
      if (!selected) return
      const cloned = await getClonedObject(selected)
      if (Array.isArray(cloned)) {
        clipboard.value = new fabric.ActiveSelection(cloned)
      } else {
        clipboard.value = cloned
      }
    },
  },
  {
    name: '剪切',
    disabled: false,
    handler: async () => {
      isShow.value = false
      clearClipboardIds()
      const selected = canvas.value.getActiveObject()
      if (!selected) return
      const cloned = await getClonedObject(selected)
      if (Array.isArray(cloned)) {
        clipboard.value = new fabric.ActiveSelection(cloned)
      } else {
        clipboard.value = cloned
      }
      deleteObject(selected, canvas.value)
    },
  },
  {
    name: '粘贴',
    disabled: false,
    handler: async (e) => {
      isShow.value = false
      if (!clipboard.value) return
      const point = canvas.value.getScenePoint(e)
      const cloned = await getClonedObject(clipboard.value)
      if (Array.isArray(cloned)) {
        const sel = new fabric.ActiveSelection(cloned)
        sel.left = point.x
        sel.top = point.y
        canvas.value.add(...cloned)
        canvas.value.setActiveObject(sel)
      } else {
        cloned.left = point.x
        cloned.top = point.y
        canvas.value.add(cloned)
        canvas.value.setActiveObject(cloned)
      }
    },
  },
  {
    name: '删除',
    disabled: false,
    handler: () => {
      isShow.value = false
      const target = canvas.value.getActiveObject()
      if (!target) return
      deleteObject(target, canvas.value)
    },
  },
  {
    name: '分组',
    disabled: false,
    handler: () => {
      isShow.value = false
      const target = canvas.value.getActiveObject()
      if (!(target.type.toLowerCase() === 'activeselection')) return
      const objects = target.removeAll()
      canvas.value.remove(...objects)
      const group = new fabric.Group(objects)
      canvas.value.add(group)
      canvas.value.setActiveObject(group)
    },
  },
  {
    name: '取消分组',
    disabled: false,
    handler: async () => {
      isShow.value = false
      const target = canvas.value.getActiveObject()
      if (!(target.type.toLowerCase() === 'group')) return
      const objects = target.removeAll()
      canvas.value.remove(target)
      canvas.value.add(...objects)
      const sel = new fabric.ActiveSelection(objects)
      canvas.value.setActiveObject(sel)
    },
  },
]

/**
 * 虽然clipboard从未真正添加到canvas，但是要清除其产生时clone生成的boss和minion的id
 */
const clearClipboardIds = () => {
  if (!clipboard.value) return
  if (clipboard.value.type.toLowerCase() === 'activeselection') {
    clipboard.value.forEachObject((o) => {
      if (o.isBoss) globalBossIds.delete(o.id)
      else if (o.isMinion) globalMinionIds.delete(o.id)
    })
  } else {
    if (clipboard.value.isBoss) globalBossIds.delete(clipboard.value.id)
    else if (clipboard.value.isMinion) globalMinionIds.delete(clipboard.value.id)
  }
}

const activeObjectDisabledList = ref(new Set(['复制', '剪切', '删除', '编辑']))
const transformStyle = ref('')

const init = () => {
  canvas.value.on('contextmenu', async ({ e }) => {
    e.preventDefault()
    e.stopPropagation()
    isShow.value = true
    left.value = e.clientX + 'px'
    top.value = e.clientY + 'px'
    nextTick(() => {
      const height = contextMenu.value.offsetHeight
      const width = contextMenu.value.offsetWidth
      const transformFuns = []
      if (e.clientY + height > window.innerHeight) {
        transformFuns.push('translateY(-100%)')
      }
      if (e.clientX + width > window.innerWidth) {
        transformFuns.push('translateX(-100%)')
      }
      transformStyle.value = transformFuns.join(' ')
    })

    /* 菜单禁用状态 */
    const activeObject = canvas.value.getActiveObject()
    const activeObjects = canvas.value.getActiveObjects()
    opterations.forEach((item) => {
      if (activeObjectDisabledList.value.has(item.name)) {
        item.disabled = !(activeObject && activeObjects.length > 0) // 如果没有选中对象，则禁用
      } else if (item.name === '粘贴') {
        item.disabled = !clipboard.value
      } else if (item.name === '分组') {
        item.disabled = !(activeObject?.type.toLowerCase() === 'activeselection')
      } else if (item.name === '取消分组') {
        item.disabled = !(activeObject?.type.toLowerCase() === 'group')
      }
    })
  })
}
onMounted(() => {
  init()
})
</script>

<style lang="scss" scoped>
$left: v-bind(left);
$top: v-bind(top);
.context-menu {
  position: fixed;
  left: $left;
  top: $top;
  min-width: 200px;
  background-color: #262933;
  padding: 10px 0;
  border-radius: 10px;
  box-shadow:
    rgba(60, 64, 67, 0.3) 0px 1px 2px 0px,
    rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
  color: #ddd;
  font-size: 14px;
  .operations {
    display: flex;
    flex-direction: column;
    .operation {
      padding: 0.5rem 0rem 0.5rem 1rem;
      cursor: pointer;
      &:hover {
        background-color: #3c4043;
      }
      &.disabled {
        color: #aaa;
        cursor: not-allowed;
        &:hover {
          background-color: inherit;
        }
      }
    }
  }
}
</style>
